
package com.thinkgem.jeesite.common.component.redis.element;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.Transaction;

/**
 * 同步锁,用于处理不同jvm场景下的redis同步问题,通常用于多客户端
 * @author Teaey
 */
public class RdsLock{

	public RdsLock(Jedis jedis, JedisPool pool, String key, boolean locked) {
	    super();
	    this.jedis = jedis;
	    this.pool = pool;
	    this.key = key;
	    this.locked = locked;
    }

	public RdsLock(Jedis jedis, JedisPool pool, String key) {
	    super();
	    this.jedis = jedis;
	    this.pool = pool;
	    this.key = key;
    }

	public RdsLock(Jedis jedis, JedisPool pool) {
	    super();
	    this.jedis = jedis;
	    this.pool = pool;
	    this.key = getKey();
    }

	public RdsLock(JedisPool pool, String key) {
	    this.jedis = pool.getResource();
	    this.pool = pool;
	    this.key = key;
    }

	public RdsLock(JedisPool pool) {
	    this.jedis = pool.getResource();
	    this.pool = pool;
	    this.key = getKey();
    }

	public Jedis getJedis() {
    	return jedis;
    }

	public void setJedis(Jedis jedis) {
    	this.jedis = jedis;
    }

	public JedisPool getPool() {
    	return pool;
    }

	public void setPool(JedisPool pool) {
    	this.pool = pool;
    }

	public String getKey() {
    	return key;
    }

	public void setKey(String key) {
    	this.key = key;
    }

	public boolean isLocked() {
    	return locked;
    }

	public void setLocked(boolean locked) {
    	this.locked = locked;
    }

	//setnx方式实现锁同步(setnx在填写相同key的value时会失败)
	public boolean lock(long timeout) {
		long nano = System.nanoTime();
		timeout *= ONE_MILLI_NANOS;
		try {
			while ((System.nanoTime() - nano) < timeout) {
				if (jedis.setnx(key, LOCKED) == 1) {
					jedis.expire(key, EXPIRE);
					locked = true;
					return locked;
				}
				// 短暂休眠，nano避免出现活锁
				Thread.sleep(3, r.nextInt(500));
			}
		} catch (Exception e) {
		}
		return false;
	}

	public boolean lock() {
		return lock(DEFAULT_TIME_OUT);
	}

	//通过事务(multi)实现
	public boolean lock_2(long timeout) {
		long nano = System.nanoTime();
		timeout *= ONE_MILLI_NANOS;
		try {
			while ((System.nanoTime() - nano) < timeout) {
				Transaction t = jedis.multi();
				// 开启事务，当server端收到multi指令
				// 会将该client的命令放入一个队列，然后依次执行，知道收到exec指令
				t.getSet(key, LOCKED);
				t.expire(key, EXPIRE);
				String ret = (String) t.exec().get(0);
				if (ret == null || ret.equals("UNLOCK")) {
					locked = true;
					return locked;
				}
				// 短暂休眠，nano避免出现活锁
				Thread.sleep(3, r.nextInt(500));
			}
		} catch (Exception e) {
		}
		return false;
	}

	public boolean lock_2() {
		return lock_2(DEFAULT_TIME_OUT);
	}

	//通过事务和监听混合方式实现
	public boolean lock_3(long timeout) {
		long nano = System.nanoTime();
		timeout *= ONE_MILLI_NANOS;
		try {
			while ((System.nanoTime() - nano) < timeout) {
				jedis.watch(key);
				// 开启watch之后，如果key的值被修改，则事务失败，exec方法返回null
				String value = jedis.get(key);
				if (value == null || value.equals("UNLOCK")) {
					Transaction t = jedis.multi();
					t.setex(key, EXPIRE, LOCKED);
					if (t.exec() != null) {
						locked = true;
						return locked;
					}
				}
				jedis.unwatch();
				// 短暂休眠，nano避免出现活锁
				Thread.sleep(3, r.nextInt(500));
			}
		} catch (Exception e) {
		}
		return false;
	}

	public boolean lock_3() {
		return lock_3(DEFAULT_TIME_OUT);
	}

	// 无论是否加锁成功，必须调用
	public void unlock() {
		try {
			if (locked)
				jedis.del(key);
		} finally {
			pool.returnResource(jedis);
		}

	}


	//redis分布式
	public static void distributeTest(String... args) {
		List<JedisShardInfo> hosts = new ArrayList<JedisShardInfo>();
        //server1
        JedisShardInfo host1 = new JedisShardInfo("", 6380, 2000);
        //server2
        JedisShardInfo host2 = new JedisShardInfo("", 6381, 2000);
        hosts.add(host1);
        hosts.add(host2);
        ShardedJedis jedis = new ShardedJedis(hosts);
        jedis.set("key", "");
        jedis.close();
	}

	// 加锁标志
	public static final String LOCKED = "TRUE";
	public static final long ONE_MILLI_NANOS = 1000000L;
	// 默认超时时间（毫秒）
	public static final long DEFAULT_TIME_OUT = 3000;
	public static final Random r = new Random();
	// 锁的超时时间（秒），过期删除
	public static final int EXPIRE = 5 * 60;

	private Jedis jedis;
	private JedisPool pool;
	private String key="rdbLocked";
	// 锁状态标志
	private boolean locked = false;
}